#include <vector>
#include <cmath>
#include "structures.h"
#include "common.h"
#include "game_bot.h"

using namespace std;

/**
 * Public constructor. Makes internal REFERENCES to all specified arguments
 */
game_bot::game_bot(const game_settings& _settings, const vector<unit_type>& _unit_types, const vector<state>& _states, const game_field& _field):
	settings(_settings), unit_types(_unit_types), states(_states), field(_field), calc(_settings, _field)
{
}

/**
 * Through the fourth and the fifth arguments, returns coordinates (adj_x; adj_y)
 * of a cell, which is is marked as reachable on the distances matrix and is
 * adjacent to the cell (x; y)
 *
 * Return value describes whether such cell was found
 */
bool game_bot::get_reachable_adjacent_cell(const vector<vector<int>>& distances, int x, int y, int& adj_x, int& adj_y)
{
	for(int i = 0; i < DN; i++)
	{
		adj_x = x + DX[i];
		adj_y = y + DY[i];
		if (inside(adj_x, adj_y, settings.field_radius) && distances[adj_x][adj_y] >= 0)
		{
			return true;
		}
	}

	return false;
}

/**
 * Returns how effective is the specified short shot
 */
double game_bot::short_shot_effect(state from, state to, state attack)
{
	//
	// TODO: implement properly
	//

	return 1;
}

/**
 * Returns the best possible short shot from the specified state
 */
move game_bot::get_best_short_shot(vector<vector<int>> distances, state from_state)
{
	//
	// TODO: ask whether damage should be divided by NSP (when needed) for
	// long-distance bots doing short shots	
	//

	move best_move(0, 0, 0, 0, 0, 0, 0);

	for (unsigned j = 0; j < states.size(); j++)
	{
		if (states[j].player_num == settings.enemy_num)
		{
			int adj_x, adj_y;
			
			// (adj_x; adj_y) is being set to the closest reachable cell
			if(get_reachable_adjacent_cell(distances, states[j].x, states[j].y, adj_x, adj_y))
			{
				move current_move(from_state.unit_count, from_state.x, from_state.y, adj_x, adj_y, states[j].x, states[j].y);
				current_move.effect = short_shot_effect(from_state, field[adj_x][adj_y], states[j]);

				if (current_move.effect > best_move.effect)
				{
					best_move = current_move;
				}
			}
		}
	}

	return best_move;
}

/**
 * Returns how effective is the specified long shot
 */
double game_bot::long_shot_effect(state from, state attack)
{
	// It doesn't matter if unit group has waited for move_interval or longer,
	// since move can be made in any case
	if (attack.since_last_move > settings.move_interval)
	{
		attack.since_last_move = settings.move_interval;
	}

	// Calculate shot damage (can't be more than health of state than is being attacked)
	double damage = unit_types[attack.unit_type_id].shot_damage;
	int total_health = unit_types[attack.unit_type_id].max_health * attack.unit_count + attack.last_unit_health;
	if (damage > total_health)
	{
		damage = total_health;
	}

	// Take FSP factor into account
	if (distance(from.x, from.y, attack.x, attack.y) > unit_types[attack.unit_type_id].shot_radius)
	{
		damage = floor(damage / settings.fsp);
	}

	// Take NSP factor into account
	for (int i = 0; i < DN; i++)
	{
		int x = from.x + DX[i], y = from.y + DY[i];
		if (inside(x, y, settings.field_radius) && field[x][y].player_num == settings.enemy_num)
		{
			damage = floor(damage / settings.nsp);
			break;
		}
	}

	return attack.since_last_move * settings.max_health + damage;
}

/**
 * Returns the best possible long shot from the specified state
 */
move game_bot::get_best_long_shot(vector<vector<int>> distances, state from_state)
{
	move best_move(0, 0, 0, 0, 0, 0, 0);

	// Short-distance units can make long shots
	if (unit_types[from_state.unit_type_id].shot_radius == 0)
	{
		return best_move;
	}

	for (unsigned j = 0; j < states.size(); j++)
	{
		if (states[j].player_num == settings.enemy_num)
		{
			move current_move(from_state.unit_count, from_state.x, from_state.y, from_state.x, from_state.y, states[j].x, states[j].y);
			current_move.effect = long_shot_effect(from_state, states[j]);

			if (current_move.effect > best_move.effect)
			{
				best_move = current_move;
			}
		}
	}

	return best_move;
}

/**
 * Tries different strategies and returns the best of all analyzed moves
 */
move game_bot::make_move(move enemy_move)
{
	move best_move(0, 0, 0, 0, 0, 0, 0);

	for (unsigned i = 0; i < states.size(); i++)
	{
		if (states[i].player_num == settings.my_num && states[i].since_last_move > settings.move_interval)
		{
			vector<vector<int>> distances;

			// Fill distancesmatrix
			calc.measure_distances(states[i].x, states[i].y, unit_types[states[i].unit_type_id].move_radius - 1, distances);

			// Try long shot strategy
			move long_shot = get_best_long_shot(distances, states[i]);
			if (long_shot.effect > best_move.effect)
			{
				best_move = long_shot;
			}

			// Try short shot strategy
			move short_shot = get_best_short_shot(distances, states[i]);
			if (short_shot.effect > best_move.effect)
			{
				best_move = short_shot;
			}
		}
	}

	return best_move;
}